#include <assert.h>
#include "hunter.h"
#include "point2.h"
#include "level.h"
#include "frame.h"
#include "game.h"
#include "animation.h"
#include "defines.h"
#include "rect.h"
#include "audio.h"
#include "source.h"
#include "io.h"
#include "fx.h"
#include "projectile.h"
#include "kolobok.h"
#include "circle.h"
#include "object.h"
#include "globals.h"
#include "ai.h"

// given vector 'v' and normal 'n'
// return projection of 'v' on the perpendicular to 'n' (sld)
//? 'n' is changed (normalized) . This can be bad, but good also - no need to normalize latter
//? if 'n' should be left as is, 's' must be normalized or 'tmp' devised by s.Mag()
bool getSlideVector(Point2* v, Point2* n, Point2* sld){

	Point2 s;	// perpendicular to the normal 'n'

	n->Normalize();
	s.x =n->y;
	s.y =-n->x;

	// get projection to (sx, sy)
	float tmp =(*v)*s;	// cos between s and v multipled by v->Mag() (cause s.Mag == 1)
	if(tmp == 0)
		return false;

	*sld =s*tmp;
	return true;
}

float adjustT(Point2* v, float t_min){

/*
	float tmp =v->Mag()*t_min;
	if(tmp >=D_TOUCH)		
		return t_min*(tmp-D_TOUCH)/tmp;

	return 0;
*/
	return t_min;
}

// 
void collideToLevelAndSlide(Level* level, Character* character, Point2& v, float dt){

	Game* game =Game::Instance();
	Rect* rect =character->cur_animation->frames[character->cur_animation->cur_frame]->body;
	Point2* position =character->position;
	Point2 n, sld;
	float t;
	Object* object;

	//  if collided
	if(game->collideRectToLevel(level, rect, position, &v, &t, &n, PLATFORMS|FLOORS|SLOPES|CIRCLES, &object)){

		// move hunter to the point of collision
		t =adjustT(&v, t);
		if (t > 0) {
			*position +=v*t;
			v -=v*t;
		}
		// if there was a collision - slide by the new normal
		if(getSlideVector(&v, &n, &sld)) {
			if(game->collideRectToLevel(level, rect, position, &sld, &t, &n, PLATFORMS|CIRCLES|SLOPES, &object)) {
				// move hunter to the point of collision
				t =adjustT(&sld, t);
				if (t > 0)
					*position +=sld*t;
			}
			else
				*position +=sld;
		}
	}
	else
		*position +=v;
}

// generate one shot
// applicable only to Hunter type
void genShoot(Level* level, Hunter* hunter){

	Rect* rect =hunter->cur_animation->frames[hunter->cur_animation->cur_frame]->body;
	Point2* position =hunter->position;

	Point2 o, v;

	// find origin and the dirrection of the shooting (o, v)
	switch(hunter->aim){

	case AIM_STRAIGHT:
		v.y =0;
		if(hunter->orientation->x <0) v.x =-1;
		else v.x =1;

		o.y =position->y +hunter->shooting_level;
		break;
	case AIM_UP:
		v.y =1;
		if(hunter->orientation->x <0) v.x =-1;
		else v.x =1;

		o.y =position->y +hunter->shooting_level_up;
		break;
	case AIM_DOWN:
		v.y =-1;
		if(hunter->orientation->x <0) v.x =-1;
		else v.x =1;

		o.y =position->y +hunter->shooting_level_down;
		break;
	}
	if(hunter->orientation->x <0){
		o.x =position->x +rect->xLo;
	}
	else{
		o.x =position->x +rect->xHi;
	}

	// scale shooting dirrection and find destination
	v.Normalize();

	// create projectile
	Projectile* projectile =new Projectile;
	projectile->layer =hunter->layer;
	projectile->dir =new Point2;
	*(projectile->dir) =v;
	projectile->position =new Point2;
	*(projectile->position) =o;
	projectile->max =500;	//?
	projectile->speed =5;	//?
	level->projectiles.push_back(projectile);

	// correct drowing of the flame
	switch(hunter->aim){

	case AIM_STRAIGHT:
		o =o +v*hunter->s_correct;
		break;
	case AIM_UP:
		o =o +v*hunter->u_correct;
		break;
	case AIM_DOWN:
		o =o +v*hunter->d_correct;
		break;
	}

	// create flame sfx
	Fx* fx =new Fx(*FX_SHOT);
	assert(fx);
	fx->quad =fx->getQuad(&o, &v);
	fx->layer =hunter->layer;
	level->effects.push_back(fx);
	
	// decrease ammo level
	hunter->ammo--;
}

void genMove(Level* level, Character* character, float dt){

	Game* game =Game::Instance();
	Rect* rect =character->cur_animation->frames[character->cur_animation->cur_frame]->body;
	Point2* position =character->position;
	Point2 n;
	Object* object;

	// if the hunter stands on any object - run in parallel to the platform
	// else - begin to fall
	if(game->standsRectAtLevel(level, rect, position, &n, &object)){

		srcPlay(object, character);
		// simple AI to avoid an obsticle
//		if(character->ai && object){
//
//			if(object->get_xLo()!=1) {	//?
//			if(character->ai->xLo >object->get_xLo())
//				character->ai->xLo =object->get_xLo();	//? not fast
//			if(character->ai->xHi <object->get_xHi())
//				character->ai->xHi =object->get_xHi();	//? not fast
//			}
//		}
		// stop falling
		character->velocity->y =0;

		// find dirrection of motion
		Point2 v;
		v.x =n.y;
		v.y =-n.x;
		if(character->orientation->x <0 && v.x>0)
		{
			v.x =-v.x;
			v.y =-v.y;
		}
		else if(character->orientation->x >0 && v.x<0)
		{
			v.x =-v.x;
			v.y =-v.y;
		}
		v.Normalize();

		// find translation vector
		v *=(character->speed*dt*0.1);	// (m/s) 100cm/1000ms
		collideToLevelAndSlide(level, character, v, dt);

		// put closer to the surface
		v.x=0;
		v.y=-STAND_TEST*2;
		float t;
		if(game->collideRectToLevel(level, rect, position, &v, &t, &n, PLATFORMS|FLOORS|SLOPES|CIRCLES, &object)) {	//? put here function that uses the fact {
			t =adjustT(&v, t);
			if (t > 0)
				*character->position +=v*t;
		}
		else
			*character->position +=v;
	}
	else
		character->setFall(0);
}

void genStill(Level* level, Character* character){

	Game* game =Game::Instance();
	Rect* rect =character->cur_animation->frames[character->cur_animation->cur_frame]->body;
	Point2 n;
	Object* object;

	// if the character is still standnig on any object - do nothing
	// else - begin to fall	
	if(game->standsRectAtLevel(level, rect, character->position, &n, &object)){

		character->velocity->y=0;
//		character->velocity->x=0;
	}
	else
		character->setFall(0);
}

void genJump(Level* level, Character* character, float dt){

	// find translation vector
	Point2 v =*(character->velocity)*dt*0.1f; // 100cm/1000ms
	collideToLevelAndSlide(level, character, v, dt);

	// if the peak was reached - begin to fall
	if(character->velocity->y <0)
		character->setFall(0);
}

void genFall(Level* level, Character* character, Point2* acc, float dt){

	Game* game =Game::Instance();
	Rect* rect =character->cur_animation->frames[character->cur_animation->cur_frame]->body;
	Point2 n;
	Object* object;

	if(character->src_fall){
		Audio* audio =Audio::Instance();
		if(!audio->isPlaying(character->src_fall->id)){
			delete character->src_fall;
			character->src_fall =NULL;
		}
	}

    // if the character stands on any object - stop falling
	// else - continue to fall
	if(game->standsRectAtLevel(level, rect, character->position, &n, &object)){
		srcPlay(object, character);
		character->setStill(AIM_STRAIGHT);
	}
	else{
		// find velocity vector
		*(character->velocity) += *acc*(dt*0.001f);
		// find translation vector
		Point2 v =*(character->velocity)*dt*0.1f; // 100cm/1000ms
		collideToLevelAndSlide(level, character, v, dt);
/*
		float t;
		if(game->collideRectToLevel(level, rect, character->position, &v, &t, &n, PLATFORMS|FLOORS|SLOPES|CIRCLES, &object)){
			t = adjustT(&v, t);
			*(character->position) +=v*t;
			v -=v*t;
			v.x =0;

			if(game->collideRectToLevel(level, rect, character->position, &v, &t, &n, PLATFORMS|FLOORS|SLOPES|CIRCLES, &object)) {
				t = adjustT(&v, t);
				*character->position +=v*t;
			}
			else
				*character->position +=v;
		}
		else
			*(character->position) +=v;
*/
	}
}
